#include "str_unit.h"
#include "internal.h"

///////Word-pointer data
#define _PTR_ERROR_START_AFTER_END 0
#define _PTR_ERROR_WRONG_WORD_LENGTH 1
#define _PTR_POINTER_MISS_THE_STRING 2
#define _PTR_WORD_TOO_LONG 3
#define _PTR_POINTER_MISS_THE_TEXT 4
#define _PTR_UNEXPECTED_LINE_BREAK 5
//Word pointer in the string
class wordPointer
{
public:
	char word[100];//actual word
	virtual	void read(FILE *file)=0;
	virtual	void write(FILE *file) const=0;
	int error;//error code for wordPointer class
	static char err_desc[20][100];
};
//Word pointer in the string
class wordPointer1D : public wordPointer
{
public:
	int start;
	int end;
	//returns, 1 on success 0 if validation failed (cf. error);
	int extractFromPairOfPtrs(char *string, char *ptrStart, char *ptrEnd);
	int extractFromPtrAndLen(char *string, char *ptrStart, int len);
	virtual	void read(FILE *file);
	virtual	void write(FILE *file) const;
	static char arg_title[55];
};
//Word pointer in the text
class wordPointer2D : public wordPointer
{
public:
	int lineStart;
	int colStart;
	int lineEnd;
	int colEnd;
	//returns, 1 on success 0 if validation failed (cf. error);
	//CouldLinesDiffer ==FALSE if lineStart must be equal lineEnd
	template <int size1, int size2> int extractFromPairOfPtrs(char text[size1][size2], int vsize, char *ptrStart, char *ptrEnd, int CouldLinesDiffer=FALSE);
	template <int size1, int size2> int extractFromPtrAndLen(char text[size1][size2], int vsize, char *ptrStart, int len);
	virtual	void read(FILE *file);
	virtual	void write(FILE *file) const;
	static char arg_title[55];
};

///////Helper functions
//Read string from file and fix 0A character in the end if any
char * read_string_raw (char *str, int size, FILE *file);
//Parse string using separators into array of tokens
template <int size1, int size2> int parse_string(const char *str, const char *seps, char tokens[size1][size2]);

///////Validating input data functions

///////Base io-functions for unit-testing
//Error values
//Array on the input too large for the function
#define _READ_INVALID_ARRAY_TOO_LARGE -1
//The actual number of array elements differs from specified in file (numerical arrays)
#define _READ_INVALID_ARRAY_CONTENT_DIFFER_FROM_COUNT -2
//When reading 3D arrays number of elements in group differs from expected
#define _READ_INVALID_GROUP_ELEMENTS_COUNT -3


///Reading input arguments
int readInt(FILE *file);
int readInt1D(FILE *file, int *arr, int size);
void readString(FILE *file, char *buffer, int size);
char readChar(FILE *file);
template <int size1,int size2> int readChar2D(FILE *file, char buffer[size1][size2]);
template <int size1,int size2,int size3> int readChar3D(FILE *file, char buffer[size1][size2][size3]);
template <class T> int readPointer1D(FILE * file, T * buffer, int size);
template <int size1,int size2> void readPCharIn2D(FILE *file, char text[size1][size2], char **ptr);
///Writing output arguments
void writeInt(FILE *file,int number);
void writeFloat(FILE *file,float number);
void writeInt1D(FILE *file,const int *arr, int size);
void writeFloat1D(FILE *file,const float *arr, int size);
template <int size1,int size2> void writeInt2D(FILE *file,const  int buffer[size1][size2], int real_size);
void writeString(FILE *file,const  char *string);
void writeOutArg(FILE *file,const  char *arg);
template <int size1,int size2> void writeChar2D(FILE *file,const  char buffer[size1][size2], int real_size);
template <int size1,int size2,int size3> void writeChar3D(FILE *file,const char buffer[size1][size2][size3], int real_size);
template <class T> void writePointer1D(FILE * file, const T * buffer, int size);

/////////////////////////Implementation of templates - needed there due to C++ template specific
//Read templates
template <int size1,int size2> int readChar2D(FILE *file, char buffer[size1][size2])
{
	int vsize=readInt(file);
	if(vsize>size1)
	{
		return _READ_INVALID_ARRAY_TOO_LARGE;
	}
	for(int i=0;i<vsize;i++)
	{
		if(read_string_raw(buffer[i],size2,file)==NULL)
		{
			return _READ_INVALID_ARRAY_CONTENT_DIFFER_FROM_COUNT;
		}
	}
	return vsize;
}
template <int size1,int size2,int size3> int readChar3D(FILE *file, char buffer[size1][size2][size3])
{
	int vsize=readInt(file);
	if(vsize>size1)
	{
		return _READ_INVALID_ARRAY_TOO_LARGE;
	}
	char tmp[(size3+1)*size2];
	char tokens[size2+1][size3+1];
	int group_size;
	int i,j;
	for(i=0;i<vsize;i++)
	{
		if(read_string_raw(tmp,(size3+1)*size2,file)==NULL)
		{
			return _READ_INVALID_ARRAY_CONTENT_DIFFER_FROM_COUNT;
		}
		group_size=parse_string<size2+1,size3+1>(tmp,"|",tokens);
		if(group_size!=size2)
		{
			return _READ_INVALID_GROUP_ELEMENTS_COUNT;
		}
		for(j=0;j<size2;j++)
			strcpy(buffer[i][j],tokens[j]);
	}
	return vsize;
}
template <class T> int readPointer1D(FILE * file, T * buffer, int size)
{
	int realsize=readInt(file);
	if(realsize>size)
	{
		return _READ_INVALID_ARRAY_TOO_LARGE;
	}
	for(int i=0;i<realsize;i++)
		buffer[i].read(file);
	return realsize;
}

template <int size1,int size2> char * readPCharIn2D(FILE *file, char text[size1][size2])
{
	char tmp[50], tmp1[3][10];
	int nLine,nCol;
	read_string_raw(tmp,50,file);
	sscanf(tmp,"%d %d",&nLine,&nCol);
	if(nLine<0 || nLine>=size1 || nCol<0 || nCol>=size2)
		return NULL;
	return text[nLine]+nCol;
}
//////////////////////////////////Write templates
template <int size1,int size2> void writeChar2D(FILE *file, const char buffer[size1][size2], int real_size)
{
	int i;
	if(real_size==0)
		fprintf(file,"|||empty|||\n");

	for(i=0;i<real_size;i++)
		fprintf(file,"%s\n",buffer[i]);
}
template <int size1,int size2,int size3> void writeChar3D(FILE *file, const char buffer[size1][size2][size3], int real_size)
{
	int i,j;
	for(i=0;i<real_size;i++)
	{
		for(j=0;j<size2-1;j++)
			fprintf(file,"%s|",buffer[i][j]);
		fprintf(file,"%s\n",buffer[i][j]);
	}
}

template <int size1,int size2> void writeInt2D(FILE *file,const  int buffer[size1][size2], int real_size)
{
	if(real_size==0)
		fprintf(file,"|||empty|||\n");

	for(int i=0;i<real_size;i++)
	{
		for(int j=0;j<size2;j++)
			fprintf(file,"%d ",buffer[i][j]);
		fprintf(file,"\n");
	}
}

template <class T> void writePointer1D(FILE * file, const T * buffer, int size)
{
	if(size==0)
		fprintf(file,"no words found\n");

	for(int i=0;i<size;i++)
		buffer[i].write(file);
	//fprintf(file,"\n");
}


//pure pointer templates
template <int size1,int size2> int wordPointer2D::extractFromPairOfPtrs(char text[size1][size2], int vsize, char *ptrStart, char *ptrEnd, int CouldLinesDiffer)
{
	int i, strLen;
	bool isFindStart=false;
	bool isFindEnd=false;
	for(i=0;i<vsize && (!isFindStart || !isFindEnd);i++)
	{
		strLen=strlen(text[i]);
		if( !isFindStart && ptrStart >= text[i] && ptrStart <= text[i]+strLen)
		{
			lineStart=i;
			colStart=ptrStart-text[i];
			isFindStart=true;
		}
		if( !isFindEnd && ptrEnd >= text[i] && ptrEnd <= text[i]+strLen)
		{
			lineEnd=i;
			colEnd=ptrEnd-text[i];
			isFindEnd=true;
		}
	}
	if(!isFindStart || !isFindEnd)
	{
		error=_PTR_POINTER_MISS_THE_TEXT;
		return 0;
	}
	if(!CouldLinesDiffer && lineStart!=lineEnd)
	{
		error=_PTR_UNEXPECTED_LINE_BREAK;
		return 0;
	}
	if(lineStart==lineEnd)
	{
		strncpy(word,ptrStart,ptrEnd-ptrStart+1);
		word[colEnd-colStart+1]=0;
	}
	else//substring crosses different lines
	{
		strcpy(word,text[lineStart]+colStart);
		for(i=lineStart+1;i<lineEnd;i++)
			strcat(word,text[i]);
		strncat(word,text[lineEnd],colEnd+1);
	}
	
	return 1;
}
template <int size1,int size2> int wordPointer2D::extractFromPtrAndLen(char text[size1][size2], int vsize, char *ptrStart, int len)
{
	int i, strLen;
	bool isFindStart=false;
	for(i=0;i<vsize && !isFindStart;i++)
	{
		strLen=strlen(text[i]);
		if( !isFindStart && ptrStart >= text[i] && ptrStart <= text[i]+strLen)
		{
			if(ptrStart+len > text[i]+strLen)
			{
				error=_PTR_WORD_TOO_LONG;
				return 0;
			}
			lineStart=lineEnd=i;
			colStart=ptrStart-text[i];
			colEnd=colStart+len-1;
			isFindStart=true;
		}
	}
	if(isFindStart)
	{
		strncpy(word,text[lineStart]+colStart,len);
		word[len]=0;
	}
	else
	{
		error=_PTR_POINTER_MISS_THE_TEXT;
		return 0;
	}
	
	return 1;
}
//other templates
//Parse string using separators into array of tokens
template <int size1, int size2> int parse_string(const char *str, const char *seps, char tokens[size1][size2])
{
	char buffer[size1*size2];
	strncpy(buffer,str,size1*(size2-1));//Trim string if it's too large
	if(strlen(str) > size1*(size2-1))
		buffer[size1*size2-1]=0;
	char *ptr=strtok(buffer,seps);
	int num=0;
	while(ptr!=NULL && num<size1)
	{
		strncpy(tokens[num],ptr,size2);//Trim string if it's too large
		if(strlen(ptr) > size2-1)
			tokens[num][size2-1]=0;
		num++;
		ptr=strtok(NULL,seps);
	}
	return num;
}